﻿using MAIModel.Commands;
using Markdig;
using Markdig.Wpf;
using OllamaSharp;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.IO;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Windows.Controls;
using System.Windows.Input;

namespace MAIModel.ViewModels
{
    /// <summary>
    /// 0、Current class:
    /// 1、Realize ：
    /// 2、Current scheme：
    /// 3、Why it is better than other schemes：
    /// 4、Technical standard / dependency：
    /// 5、Reference：
    /// </summary>
    public class ChatMdViewModel : INotifyPropertyChanged
    {
        #region Field | Property | Collection | Command

        #region Field
        private string? _inputText;                  //User input text.
        private Chat? chat;                          //Build interactive chat.
        private ShareOllamaObject _ollama;           //share Ollama object.
        private CancellationTokenSource _cancellationTokenSource;   //Termination chat Token
       
        private bool _useExtensions = true;         //whether enable Markdown extensions function.
        private string _markdownContent;            //Markdown context.

        private MarkdownViewer markdownViewer;      //Markdwon viewer.
        private bool _isAutoScrolling = false;      //whather enable scroll 

        private double _textWidth;                     // MarkdownViewer width
        #endregion

        #region Property : Support property changed notify. 
        //InputText：用户输入的文本，支持属性更改通知。
        public string? InputText
        {
            get => _inputText;
            set
            {
                _inputText = value;
                OnPropertyChanged();
            }
        }
        public string MarkdownContent
        {
            get => _markdownContent;
            set
            {
                _markdownContent = value;
                // Notify property changed if needed
                OnPropertyChanged();
            }
        }


        public double TextWidth
        {
            get => _textWidth;
            set
            {
                _textWidth = value;
                OnPropertyChanged();
            }
        }
        #endregion

        #region Collection:
        #endregion

        #region Command: Builde Command： generate response command
        public ICommand? SubmitQuestionCommand { get; }
        //stop current chat
        public ICommand? StopCurrentChatCommand { get; }
        //new chat
        public ICommand? NewSessionCommand { get; }
        //scroll to MarkdownViewer  end  command
        public ICommand ScrollToEndCommand { get; }

        #endregion

        #endregion

        #region Constructor : Initialize
        public ChatMdViewModel()
        {
            // initialize object
            markdownViewer = new MarkdownViewer();
            _cancellationTokenSource = new CancellationTokenSource();

            //generate command
            SubmitQuestionCommand = new ParameterlessCommand(async()=>OnSubmitQuestion());
            StopCurrentChatCommand = new ParameterlessCommand( OnStopCurrentChat);
            NewSessionCommand = new ParameterlessCommand(OnNewSessionCommand);

            //markdown reletive command
            ScrollToEndCommand = new ScrollViewerCommand(OnScrollToEnd);
            
            OnLoadRecord();
        }
        #endregion

        #region other method

        #region other
        //setting Ollama
        public void SetOllama(ShareOllamaObject ollama)
        {
            _ollama = ollama;
        }
        //check chat state
        private bool CheckChatState()
        {
            if (_ollama.Ollama == null || _ollama.OllamaEnabled == false)
            {
                MarkdownContent += "服务未打开...";
                return false;
            }
            if (_ollama.Ollama.SelectedModel == null)
            {
                MarkdownContent += "模型未选择...";
                return false;
            }
            if (string.IsNullOrWhiteSpace(InputText))
            {
                MarkdownContent += "文本为空...";
                return false;
            }
            return true;
        }

        //trigger sroll to end
        private void OnScrollToEnd(object parameter)
        {
            var scrollViewer = parameter as ScrollViewer;
            if (scrollViewer != null && _isAutoScrolling)
            {
                scrollViewer.ScrollToEnd();
                TextWidth = scrollViewer.Width;
            }
        }
        #endregion

        #region Mardown command binding method 
        //loaded history record
        public void OnLoadRecord()
        {
            OutText(File.ReadAllText($"{Environment.CurrentDirectory}//Data//" +
                $"{DateTime.Today.ToString("yyyyMMdd")}//{DateTime.Today.ToString("yyyyMMdd")}_0.txt"));
        }

        #endregion

        #endregion

        #region command method
        /// <summary>
        /// Submit question:  Submit problem to the AI and get the output result
        /// </summary>
        private async void OnSubmitQuestion()
        {
            try
            {
                // Checks whether the string is empty, empty, or contains only whitespace characters
                if (CheckChatState())      
                {
                    _isAutoScrolling = true;            //enable auto scroll
                    //ToggleExtensions();
                    string input = InputText;
                    InputText =string.Empty;
                    string output = string.Empty;
                    OutText($"{Environment.NewLine}");
                    OutText($"## 【User】{Environment.NewLine}");
                    OutText($">{input}{Environment.NewLine}");
                    OutText($"## 【AI】{Environment.NewLine}");
                    //
                    output+=($"{Environment.NewLine}");
                    output += ($"## 【User】{Environment.NewLine}");
                    output += ($">{input}{Environment.NewLine}");
                    output += ($"## 【AI】{Environment.NewLine}");

                    if (input.Equals("/clearContext"))
                    {
                        chat = new Chat(_ollama.Ollama);
                        _ollama.RecordIndex++;
                        return;
                    }
                    #region Start answer :Mode two => chat mode 
                    if (chat == null)
                    {
                        chat = new Chat(_ollama.Ollama);
                        _ollama.RecordIndex++;
                    }
                    _cancellationTokenSource = new CancellationTokenSource();
                    await foreach (var answerToken in chat.SendAsync(input, _cancellationTokenSource.Token))
                    {
                        
                        OutText(answerToken);
                        output += (answerToken);
                        await Task.Delay(20);
                        Debug.Print(answerToken);
                    }
                    OutText($"{Environment.NewLine}");
                    _ollama.WriteDataToFileAsync(output);
                    #endregion
                }
            }
            catch (Exception ex)
            {
                OutText($"Error: {ex.Message}{Environment.NewLine}");
            }
            _isAutoScrolling = false;
        }

        /// <summary>
        /// New build chat.
        /// </summary>
        private void OnNewSessionCommand()
        {
            OnStopCurrentChat();
            if (chat != null)
            {
                chat.SendAsync("/clearContext");
                if (_ollama != null)
                    chat = new Chat(_ollama.Ollama);
            }
            OutText( $"{string.Empty}{Environment.NewLine}");
        }
        /// <summary>
        /// stop chat.
        /// </summary>
        private void OnStopCurrentChat()
        {
            _cancellationTokenSource?.Cancel();
            Task.Delay(100);
            OutText($"{string.Empty}{Environment.NewLine}");
            MarkdownContent = string.Empty;
        }
        /// <summary>
        /// output Text to Markdown.
        /// </summary>
        /// <param name="text"></param>
        public void OutText(string text)
        {
            MarkdownContent += text;
        }
        #endregion

        #region Method that trigger a property changed event.
        /// <summary>
        /// OnPropertyChanged：Trigger a property changed event.
        /// </summary>
        public event PropertyChangedEventHandler? PropertyChanged;
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}
